home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1996 July / EnigmA AMIGA RUN 09 (1996)(G.R. Edizioni)(IT)[!][issue 1996-07 & 08][EARSAN CD VIII].iso / earcd / util3 / fiflb382.lha / fifo.c < prev    next >
C/C++ Source or Header  |  1996-05-13  |  10KB  |  477 lines

  1.  
  2. /*
  3.  *  FIFO.C
  4.  */
  5.  
  6. #include "defs.h"
  7.  
  8. #if !defined(__GNUC__)
  9. #define __inline__
  10. #endif
  11.  
  12. __inline__ static void SetEOF(Fifo *);
  13. void ReCalcReaderIdx(Fifo *);
  14. void FixFiFlags(Fifo *);
  15. LibCall void RequestFifo(FHan *, Message *, long);
  16. long AvailWBufSpace(Fifo *);
  17.  
  18. #if defined(__GNUC__)
  19. /*  Even works without optimization */
  20. #define BitTestSet(p,b) \
  21. ({ char __bittestset; \
  22.   __asm__ volatile ("bsetb %0,%2@; sne %0" \
  23.             : "=d" (__bittestset) : "0" (b), "a" (p) : "cc", "memory"); \
  24.   __bittestset; \
  25. })
  26. #else
  27. __stkargs
  28. short BitTestSet(char *, short);
  29. #endif
  30.  
  31. /*
  32.  *  Open up a new fifo by name.
  33.  */
  34.  
  35. LibCall FHan *
  36. OpenFifo(char *name, long bytes, long flags)
  37. {
  38.     Fifo *fifo;
  39.     FHan *fhan;
  40.  
  41.     {                /*  bytes a power of 2? */
  42.     unsigned long i = 8;
  43.     while (i) {
  44.         if (bytes == i)
  45.         break;
  46.         i = i << 1;
  47.     }
  48.     if (i == 0)
  49.         return(NULL);
  50.     }
  51.     if ((fhan = AllocMem(sizeof(FHan), MEMF_PUBLIC | MEMF_CLEAR))) {
  52.     Forbid();
  53.     if (NULL == (fifo = (Fifo *)FindName((MaxList *)&FifoList, name))) {
  54.         if ((fifo = AllocMem(sizeof(Fifo) - sizeof(fifo->fi_Buf) + bytes + strlen(name) + 1, MEMF_PUBLIC | MEMF_CLEAR))) {
  55.         AddTail((MaxList *)&FifoList, &fifo->fi_Node);
  56.         NewList((MaxList *)&fifo->fi_HanList);
  57.         NewList((MaxList *)&fifo->fi_WrNotify);
  58.         NewList((MaxList *)&fifo->fi_RdNotify);
  59.         fifo->fi_BufSize = bytes;
  60.         fifo->fi_BufMask = bytes - 1;
  61.         fifo->fi_Node.ln_Name = (char *)fifo + (sizeof(Fifo) - sizeof(fifo->fi_Buf)) + bytes;
  62.         strcpy(fifo->fi_Node.ln_Name, name);
  63.         InitSemaphore(&fifo->fi_SigSem);
  64.         }
  65.     }
  66.     if (fifo) {
  67.         AddTail((MaxList *)&fifo->fi_HanList, (MaxNode *)&fhan->fh_Node);
  68.         fhan->fh_Fifo = fifo;
  69.         fhan->fh_Flags = flags;
  70.  
  71.         fhan->fh_Msg.mn_ReplyPort = &fhan->fh_Port;
  72.  
  73.         fhan->fh_Port.mp_Node.ln_Type = NT_MSGPORT;
  74.         fhan->fh_Port.mp_Flags = PA_SIGNAL;
  75.         fhan->fh_Port.mp_SigBit = SIGB_SINGLE;
  76.  
  77.         NewList(&fhan->fh_Port.mp_MsgList);
  78.  
  79.         if (flags & FIFOF_READ) {
  80.         ++fifo->fi_RRefs;
  81.         fhan->fh_RIdx = fifo->fi_RIdx;
  82.         }
  83.         if (flags & FIFOF_WRITE) {
  84.         ++fifo->fi_WRefs;
  85.         }
  86.         /*
  87.         if (flags & FIFOF_CLOSEOF)
  88.         fifo->fi_Flags |= FIFOF_CLOSEOF;
  89.         */
  90.         if (flags & FIFOF_KEEPIFD)
  91.         fifo->fi_Flags |= FIFOF_KEEPIFD;
  92.         if (flags & FIFOF_RREQUIRED)
  93.         fifo->fi_Flags |= FIFOF_RREQUIRED;
  94.         ++fifo->fi_ORefs;
  95.         FixFiFlags(fifo);
  96.     } else {
  97.         FreeMem(fhan, sizeof(FHan));
  98.         fhan = NULL;
  99.     }
  100.     Permit();
  101.     }
  102.     return(fhan);
  103. }
  104.  
  105. /*
  106.  *  SetEOF()
  107.  */
  108.  
  109. __inline__ static
  110. void
  111. SetEOF(Fifo *fifo)
  112. {
  113.     FHan *fh;
  114.  
  115.     for (fh = (FHan *)fifo->fi_HanList.mlh_Head; fh->fh_Node.mln_Succ; fh = (FHan *)fh->fh_Node.mln_Succ)
  116.     fh->fh_Flags |= FIFOF_EOF;
  117.     fifo->fi_Flags |= FIFOF_EOF;
  118. }
  119.  
  120. /*
  121.  *  Close a previously opened fifo
  122.  */
  123.  
  124. LibCall void
  125. CloseFifo(FHan *fhan, long flags)
  126. {
  127.     Fifo *fifo;
  128.  
  129.     Forbid();
  130.     if ((fifo = fhan->fh_Fifo)) {
  131.     fhan->fh_Fifo = NULL;    /*  try to catch duplicate closes   */
  132.     Remove((MaxNode *)&fhan->fh_Node);
  133.  
  134.     --fifo->fi_ORefs;
  135.  
  136.     if (fhan->fh_Flags & FIFOF_WRITE) {
  137.         if (flags & FIFOF_EOF)
  138.         fifo->fi_Flags |= FIFOF_CLOSEOF;
  139.         if (--fifo->fi_WRefs == 0) {
  140.         if (fifo->fi_Flags & FIFOF_CLOSEOF) {
  141.             SetEOF(fifo);
  142.         }
  143.         }
  144.     }
  145.     if (fhan->fh_Flags & FIFOF_READ) {
  146.         --fifo->fi_RRefs;
  147.  
  148.         if (fifo->fi_RRefs == 0 && (fifo->fi_Flags & FIFOF_RREQUIRED)) {
  149.         Message *msg;
  150.         while ((msg = (Message *)RemHead((MaxList *)&fifo->fi_WrNotify)))
  151.             ReplyMsg(msg);
  152.         }
  153.     }
  154.  
  155.     if (fifo->fi_ORefs == 0) {
  156.         if ((fifo->fi_Flags & FIFOF_KEEPIFD) == 0 || fifo->fi_RIdx == fifo->fi_WIdx) {
  157.         Remove(&fifo->fi_Node);
  158.         FreeMem(fifo, sizeof(Fifo) - sizeof(fifo->fi_Buf) + fifo->fi_BufSize + strlen(fifo->fi_Node.ln_Name) + 1);
  159.         }
  160.     } else {
  161.         if (fhan->fh_Flags & FIFOF_WRITE && fifo->fi_Flags & FIFOF_EOF) {
  162.         Message *msg;
  163.  
  164.         /*  signal EOF to any blocked readers */
  165.         while ((msg = (Message *)RemHead((MaxList *)&fifo->fi_RdNotify)))
  166.             ReplyMsg(msg);
  167.         }
  168.         if (fhan->fh_Flags & FIFOF_READ)
  169.         ReCalcReaderIdx(fifo);
  170.         FixFiFlags(fifo);
  171.     }
  172.     FreeMem(fhan, sizeof(FHan));
  173.     }
  174.     Permit();
  175. }
  176.  
  177. /*
  178.  *  Read from a fifo.  Block if necessary (and not FIFOF_NBIO)
  179.  */
  180.  
  181. LibCall long
  182. ReadFifo(FHan *fhan, char **pbuf, long skip)
  183. {
  184.     long n;
  185.     Fifo *fifo = fhan->fh_Fifo;
  186.  
  187.     /*
  188.      *    attempt to clear <skip> bytes
  189.      */
  190.  
  191.     while (skip > 0) {
  192.     long ridx = fhan->fh_RIdx;
  193.     long len;
  194.  
  195.     len = fifo->fi_WIdx - ridx;
  196.     if (len < 0)
  197.         len = fifo->fi_BufSize - ridx;
  198.     if (len == 0)
  199.         break;
  200.     if (len > skip)
  201.         len = skip;
  202.     /* n += len; --unused */
  203.     skip -= len;
  204.  
  205.     Forbid();
  206.     fhan->fh_RIdx = (ridx + len) & fifo->fi_BufMask;
  207.     if (ridx == fifo->fi_RIdx)
  208.         ReCalcReaderIdx(fifo);      /*  update mast-idx/writer-waiters */
  209.     Permit();
  210.     }
  211.  
  212.     /*
  213.      *    return available data
  214.      */
  215.  
  216.     for (;;) {
  217.     long ridx = fhan->fh_RIdx;
  218.  
  219.     n = fifo->fi_WIdx - ridx;
  220.     if (n < 0)
  221.         n = fifo->fi_BufSize - ridx;
  222.     *pbuf = fifo->fi_Buf + ridx;
  223.     if (n == 0) {
  224.         /*
  225.          *    EOF on a per-handle basis since it gets cleared after EOF
  226.          *    is returned.
  227.          */
  228.  
  229.         if ((fhan->fh_Flags & FIFOF_EOF) || (fifo->fi_Flags & FIFOF_EOF)) {
  230.         /*fhan->fh_Flags &= ~FIFOF_EOF;*/
  231.         n = -1;
  232.         break;
  233.         }
  234.         if ((fhan->fh_Flags & FIFOF_NBIO) == 0) {
  235.         fhan->fh_Port.mp_SigTask = SysBase->ThisTask;
  236.         RequestFifo(fhan, &fhan->fh_Msg, FREQ_RPEND);
  237.         WaitPort(&fhan->fh_Port);
  238.         Remove((MaxNode *)&fhan->fh_Msg);
  239.         continue;
  240.         }
  241.     }
  242.     break;
  243.     }
  244.     return(n);
  245. }
  246.  
  247. /*
  248.  *  Write to a fifo
  249.  */
  250.  
  251. LibCall long
  252. WriteFifo(FHan *fhan, char *buf, long bytes)
  253. {
  254.     long n = -1;
  255.     short wsigchk = 0;
  256.     short normal  = 0;
  257.     Fifo *fifo = fhan->fh_Fifo;
  258.  
  259.     if (fifo->fi_RRefs == 0 && (fifo->fi_Flags & FIFOF_RREQUIRED))
  260.     return(-1);
  261.  
  262.     if (bytes < 0 || bytes > (fifo->fi_BufSize >> 1))
  263.     return(-2);
  264.  
  265.     Forbid();
  266.  
  267.     /*
  268.      *    A normal FIFO uses fi_SigSem
  269.      *    A non-normal FIFO cannot afford to block and uses fi_Lock
  270.      */
  271.  
  272.     if (fifo->fi_Flags & FIFOF_WRNORM)
  273.     normal = 1;
  274.  
  275.     if (normal) {
  276.     ObtainSemaphore(&fifo->fi_SigSem);
  277.     } else if (BitTestSet(&fifo->fi_Lock, 0) != 0) {
  278.     Permit();
  279.     return(n);
  280.     }
  281.  
  282.     {
  283.     n = 0;
  284.  
  285.     for (;;) {
  286.         while (bytes) {
  287.         long ridx = fifo->fi_RIdx;    /*  snapshot ridx   */
  288.         long len;
  289.  
  290.         if (fifo->fi_WIdx < ridx) {
  291.             len = ridx - fifo->fi_WIdx;
  292.             if (len <= bytes)           /*  FIFO FULL       */
  293.             break;
  294.         } else {
  295.             len = fifo->fi_BufSize - fifo->fi_WIdx;
  296.             if (len + ridx <= bytes)    /*  FIFO FULL       */
  297.             break;
  298.         }
  299.         if (len > bytes)
  300.             len = bytes;
  301.         CopyMem(buf, fifo->fi_Buf + fifo->fi_WIdx, len);
  302.         buf += len;
  303.         n += len;
  304.         bytes -= len;
  305.         fifo->fi_WIdx = (fifo->fi_WIdx + len) & fifo->fi_BufMask;
  306.         if (fhan->fh_Flags & FIFOF_NORMAL)
  307.             wsigchk = 1;
  308.         }
  309.  
  310.         /*
  311.          *    if fifo is full and NBIO not set, then block and loop
  312.          */
  313.         if (bytes && !(fhan->fh_Flags & FIFOF_NBIO)) {
  314.         fhan->fh_Port.mp_SigTask = SysBase->ThisTask;
  315.         RequestFifo(fhan, &fhan->fh_Msg, FREQ_WAVAIL);
  316.         WaitPort(&fhan->fh_Port);
  317.         Remove((MaxNode *)&fhan->fh_Msg);
  318.         continue;
  319.         }
  320.         break;
  321.     }
  322.  
  323.     /*
  324.      *  check for any blocked readers, data is probably now available
  325.      *  so wake them up.
  326.      */
  327.  
  328.     if (wsigchk && fifo->fi_RdNotify.mlh_Head->mln_Succ) {
  329.         Message *msg;
  330.  
  331.         while ((msg = (Message *)RemHead((MaxList *)&fifo->fi_RdNotify)))
  332.         ReplyMsg(msg);
  333.     }
  334.  
  335.     if (normal)
  336.         ReleaseSemaphore(&fifo->fi_SigSem);
  337.     else
  338.         fifo->fi_Lock = 0;
  339.     }
  340.     Permit();
  341.     return(n);
  342. }
  343.  
  344. LibCall long
  345. BufSizeFifo(FHan *fhan)
  346. {
  347.     return(fhan->fh_Fifo->fi_BufSize);
  348. }
  349.  
  350.  
  351. /*
  352.  *  Calculate distance between fh->fh_RIdx and fifo->fi_RIdx, shortest
  353.  *  wins.  If none found then nothing changes due to initial best set
  354.  *  to exactly the buffer size.
  355.  *
  356.  *  If the master index is modified and writers are waiting, signal them.
  357.  *
  358.  *  Called while Forbid() or otherwise reader-lockedout
  359.  */
  360.  
  361. void
  362. ReCalcReaderIdx(Fifo *fifo)
  363. {
  364.     FHan *fh;
  365.     long bestLen = fifo->fi_BufSize;
  366.     long ridx;
  367.  
  368.     for (fh = (FHan *)fifo->fi_HanList.mlh_Head; fh->fh_Node.mln_Succ; fh = (FHan *)fh->fh_Node.mln_Succ) {
  369.     if (fh->fh_Flags & FIFOF_READ) {
  370.         long len = (fh->fh_RIdx - fifo->fi_RIdx) & fifo->fi_BufMask;
  371.         if (len < bestLen)
  372.         bestLen = len;
  373.     }
  374.     }
  375.  
  376.     ridx = (fifo->fi_RIdx + bestLen) & fifo->fi_BufMask;
  377.  
  378.     /*
  379.      *    more buffer space available, wakeup writers?
  380.      */
  381.  
  382.     if (ridx != fifo->fi_RIdx) {
  383.     fifo->fi_RIdx = ridx;
  384.  
  385.     if (fifo->fi_WrNotify.mlh_Head->mln_Succ) {
  386.         Message *msg;
  387.  
  388.         if (AvailWBufSpace(fifo) >= (fifo->fi_BufSize >> 1)) {
  389.         while ((msg = (Message *)RemHead((MaxList *)&fifo->fi_WrNotify)))
  390.             ReplyMsg(msg);
  391.         }
  392.     }
  393.     }
  394. }
  395.  
  396. /*
  397.  *  FIXME, if state change occurs in master fifo, must unblock anybody
  398.  *  blocked due to previous information. XXX
  399.  */
  400.  
  401. void
  402. FixFiFlags(Fifo *fifo)
  403. {
  404.     FHan *fh;
  405.     long rflags = FIFOF_RDNORM | FIFOF_WRNORM;
  406.  
  407.     fifo->fi_Flags &= ~rflags;
  408.     for (fh = (FHan *)fifo->fi_HanList.mlh_Head; fh->fh_Node.mln_Succ; fh = (FHan *)fh->fh_Node.mln_Succ) {
  409.     if ((fh->fh_Flags & FIFOF_NORMAL) == 0) {
  410.         if (fh->fh_Flags & FIFOF_READ)
  411.         rflags &= ~FIFOF_RDNORM;
  412.         if (fh->fh_Flags & FIFOF_WRITE)
  413.         rflags &= ~FIFOF_WRNORM;
  414.     }
  415.     }
  416.     fifo->fi_Flags |= rflags;
  417. }
  418.  
  419. /*
  420.  *  request message on event.  Returns message immediately if event already
  421.  *  satisfied.
  422.  */
  423.  
  424. LibCall void
  425. RequestFifo(FHan *fhan, Message *msg, long req)
  426. {
  427.     Fifo *fifo = fhan->fh_Fifo;
  428.  
  429.     Forbid();
  430.  
  431.     switch(req) {
  432.     case FREQ_RPEND:
  433.     if ((fhan->fh_Flags & FIFOF_EOF) || fhan->fh_RIdx != fifo->fi_WIdx) {
  434.         ReplyMsg(msg);
  435.     } else {
  436.         msg->mn_Node.ln_Type = NT_MESSAGE;
  437.         AddTail((MaxList *)&fifo->fi_RdNotify, &msg->mn_Node);
  438.     }
  439.     break;
  440.     case FREQ_WAVAIL:
  441.     /*
  442.      *  determine available buffer space, alert if more than 1/2 empty.
  443.      *
  444.      *  check for broken pipe
  445.      */
  446.  
  447.     if (fifo->fi_RRefs == 0 && (fifo->fi_Flags & FIFOF_RREQUIRED)) {
  448.         ReplyMsg(msg);
  449.     } else if (AvailWBufSpace(fifo) >= (fifo->fi_BufSize >> 1)) {
  450.         ReplyMsg(msg);
  451.     } else {
  452.         msg->mn_Node.ln_Type = NT_MESSAGE;
  453.         AddTail((MaxList *)&fifo->fi_WrNotify, &msg->mn_Node);
  454.     }
  455.     break;
  456.     case FREQ_ABORT:
  457.     if (msg->mn_Node.ln_Type == NT_MESSAGE) {   /*  if not returned */
  458.         Remove(&msg->mn_Node);                  /*  return it       */
  459.         ReplyMsg(msg);
  460.     }
  461.     break;
  462.     }
  463.     Permit();
  464. }
  465.  
  466. long
  467. AvailWBufSpace(Fifo *fifo)
  468. {
  469.     long ridx = fifo->fi_RIdx;
  470.     long len;
  471.  
  472.     len = ridx - fifo->fi_WIdx;
  473.     if (len <= 0)
  474.     len = fifo->fi_BufSize - fifo->fi_WIdx + ridx - 1;
  475.     return(len);
  476. }
  477.